use std::io;
use std::io::{BufRead, BufReader, Read, Write};
use std::net::{Shutdown, TcpListener, TcpStream};
use std::time::Duration;

fn main() -> std::io::Result<()> {
    let mut stream = TcpStream::connect("127.0.0.1:7788")?;

    //  为底层套接字创建一个新的独立的拥有所有权的句柄。
    // 返回的 TcpStream 是与此对象引用相同的流的引用。 两个句柄将读取和写入相同的数据流，并且在一个流上设置的选项将传播到另一流。
    let _stream_clone = stream.try_clone().expect("clone failed...");

    // 设置读取超时
    let five_seconds = Duration::new(5, 0);
    stream.set_read_timeout(Option::from(five_seconds)).expect("set_read_timeout failed...");
    // stream.set_read_timeout(None).expect("set_read_timeout failed...");

    // 将写超时设置为指定的超时。
    stream.set_write_timeout(None).expect("set_write_timeout failed...");

    // let result = stream.set_write_timeout(Some(Duration::new(0, 0)));
    // let err = result.unwrap_err();
    // assert_eq!(err.kind(), io::ErrorKind::InvalidInput);

    //返回此套接字的读取超时。
    if let Some(timeout) = stream.read_timeout()? {
        println!("read_timeout = {:?}", timeout);
    }
    // 返回此套接字的写入超时。
    if let Some(timeout) = stream.write_timeout()? {
        println!("write_timeout = {:?}", timeout);
    }

    if let Ok(stream) = TcpStream::connect("127.0.0.1:7788") {
        println!("Connected to the server!");
    } else {
        println!("Couldn't connect to server...");
    }

    // 返回此 TCP 连接的远程对等方的套接字地址。
    let local_ip = stream.local_addr().unwrap();
    println!("local_ip = {}", local_ip);
    // 返回此 TCP 连接的远程对等方的套接字地址。
    let remote_ip = stream.peer_addr().unwrap();
    println!("remote_ip = {}", remote_ip);

    // 从套接字所连接的远程地址接收套接字上的数据，而无需从队列中删除该数据。
    // let mut buf = [0; 10];
    // let len = stream.peek(&mut buf).expect("peek failed");
    // println!("peeked {} bytes", len);
    // println!("{:?}", buf);

    //设置此套接字上 SO_LINGER 选项的值。
    // 此值控制当数据仍有待发送时如何关闭套接字。 如果设置了 SO_LINGER，则当系统尝试发送挂起的数据时，套接字将在指定的持续时间内保持打开状态。
    // stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger call failed");


    //设置此套接字上 TCP_NODELAY 选项的值。
    // 如果设置，则此选项禁用 Nagle 算法。 这意味着即使只有少量数据，也总是尽快发送段。 如果未设置，则对数据进行缓冲，直到有足够的数据量可以发送出去，从而避免了频繁发送小数据包
    stream.set_nodelay(true).expect("set_nodelay call failed");

    // 获取此套接字上 TCP_NODELAY 选项的值。
    println!("nodelay = {}", stream.nodelay()?);


    // 设置此套接字上 IP_TTL 选项的值。 TL的最大值是255，TTL的一个推荐值是64。,
    // 此值设置从该套接字发送的每个数据包中使用的生存时间字段。
    // 在IP数据包从源到目的的整个转发路径上，每经过一个路由器，路由器都会修改这个TTL字段值，具体的做法是把该TTL的值减1，然后再将IP包转发出去。
    stream.set_ttl(10).expect("set_ttl call failed");

    // 获取此套接字的 IP_TTL 选项的值。
    println!("ttl = {}", stream.ttl()?);

    //获取此套接字上 SO_ERROR 选项的值。
    // 这将检索底层套接字中存储的错误，从而清除进程中的字段。 这对于检查两次调用之间的错误很有用。
    if let Some(err) = stream.take_error()? {
        println!("error = {:?}", err);
    }


    // 将此 TCP 流移入或移出非阻塞模式
    // stream.set_nonblocking(true).expect("set_nonblocking call failed");

    // let mut buf = vec![];
    // loop {
    //     match stream.read_to_end(&mut buf) {
    //         Ok(_) => {
    //             println!("bytes: {buf:?}");
    //
    //             println!("{:?}", stream);
    //             stream.write(b"hello").unwrap();
    //             break;
    //         },
    //         Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
    //             // 等待网络套接字就绪，通常通过平台特定的 API (例如 epoll 或 IOCP) 实现
    //             // wait_for_fd();
    //         }
    //         Err(e) => panic!("encountered IO error: {e}"),
    //     };
    // };
    // println!("bytes: {buf:?}");

    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("Failed to read");
    stream.write(input.as_bytes()).expect("Failed to write");

    // let mut reader = BufReader::new(&stream);
    // let mut buffer: Vec<u8> = Vec::new();
    // reader.read_until(b'\n', &mut buffer).expect("Failed to read into buffer");
    // println!("read  from server: {}", str::from_utf8(&buffer).unwrap());332
    println!("end");

    println!("Hello, world!");


    // println!("{:?}", stream);
    // stream.write(b"hello").unwrap();
    // 标准输出
    // stream.read(&[1]).unwrap();
    // stream.flush().unwrap();

    // 关闭此连接的读取，写入或两半。
    // 此函数将导致指定部分上的所有未决和 future I/O 立即以适当的值返回
    // stream.shutdown(Shutdown::Both).expect("shutdown call failed");

    Ok(())
}

